package com.fjec.common.wx;

import com.alibaba.fastjson.JSON;
import com.fjec.common.Const;
import com.fjec.common.util.Base64Util;
import com.fjec.common.util.JsonUtil;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.enums.TradeType;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.model.OrderQueryModel;
import com.ijpay.wxpay.model.ProfitSharingModel;
import com.ijpay.wxpay.model.RefundModel;
import com.ijpay.wxpay.model.UnifiedOrderModel;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;

import java.io.ByteArrayInputStream;
import java.util.List;
import java.util.Map;

@Slf4j
public class WxPayHelper {

    private WxAccessTokenHelper wxAccessTokenHelper;

    private WxProperties properties;

    public WxPayHelper(WxAccessTokenHelper wxAccessTokenHelper) {
        this.wxAccessTokenHelper = wxAccessTokenHelper;
        this.properties = wxAccessTokenHelper.getProperties();
    }

    /**
     * 微信预支付订单
     *
     * @param openId    用户openId
     * @param ip        用户ip地址
     * @param amount    总价格（分）
     * @param orderNum  订单编号
     * @param attach    附加数据，在查询API和支付通知中原样返回，可作为自定义参数使用。
     * @param notifyUrl 异步接收微信支付结果通知的回调地址，通知url必须为外网可访问的url，不能携带参数。
     * @param body      商品简单描述，该字段请按照规范传递，详细参见：https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_2
     * @return
     * @throws Exception
     */
    public Map<String, String> prepareMiniAppPay(String openId, String ip, String amount, String orderNum, String attach,
                                                 String notifyUrl, String body) throws Exception {
        Map<String, String> params = UnifiedOrderModel
                .builder()
                .appid(properties.getAppId())
                .mch_id(properties.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .body(body)
                .attach(attach)
                .out_trade_no(orderNum)
                .total_fee(amount)
                .spbill_create_ip(ip)
                .notify_url(notifyUrl)
                .trade_type(TradeType.JSAPI.getTradeType())
                .openid(openId)
                .build()
                .createSign(properties.getApiKey(), SignType.HMACSHA256);
        String returnStr = WxPayApi.pushOrder(false, params);
        return handleWxResult(returnStr);
    }

    /**
     * 微信拉起支付
     *
     * @param prepayId
     * @return
     */
    public String miniAppPay(String prepayId) {
        Map<String, String> packageParams = WxPayKit.miniAppPrepayIdCreateSign(
                properties.getAppId(),
                prepayId,
                properties.getApiKey(),
                SignType.HMACSHA256);
        String jsonStr = JSON.toJSONString(packageParams);
        return jsonStr;
    }

    /**
     * 申请退款
     *
     * @param transactionId   微信生成的订单号，在支付通知中有返回
     * @param outTradeNo      商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|*@ ，且在同一个商户号下唯一。
     *                        transaction_id、out_trade_no二选一，如果同时存在优先级：transaction_id> out_trade_no
     * @param outRefundNo     商户系统内部的退款单号，商户系统内部唯一，只能是数字、大小写字母_-|*@ ，同一退款单号多次请求只退一笔。
     * @param totalFee        订单总金额，单位为分，只能为整数
     * @param refundFee       退款总金额，订单总金额，单位为分，只能为整数
     * @param refundNotifyUrl 异步接收微信支付退款结果通知的回调地址，通知URL必须为外网可访问的url，
     *                        不允许带参数如果参数中传了notify_url，则商户平台上配置的回调地址将不会生效。
     * @return
     */
    public Map<String, String> refund(String transactionId, String outTradeNo, String outRefundNo,
                                      String totalFee, String refundFee, String refundNotifyUrl) {
        byte[] certBytes = Base64Util.decode2Bytes(properties.getCertBase64());
        ByteArrayInputStream certInputStream = new ByteArrayInputStream(certBytes);
        Map<String, String> params = RefundModel.builder()
                .appid(properties.getAppId())
                .mch_id(properties.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .transaction_id(transactionId)
                .out_trade_no(outTradeNo)
                .out_refund_no(outRefundNo)
                .total_fee(totalFee)
                .refund_fee(refundFee)
                .notify_url(refundNotifyUrl)
                .build()
                .createSign(properties.getApiKey(), SignType.MD5);
        String returnStr = WxPayApi.orderRefund(false, params, certInputStream, properties.getCertPwd());
        return handleWxResult(returnStr);
    }

    /**
     * 请求多次分账
     *
     * @param transactionId 微信支付订单号
     * @param outOrderNo    商户系统内部的分账单号，在商户系统内部唯一（单次分账、多次分账、完结分账应使用不同的商户分账单号），同一分账单号多次请求等同一次。只能是数字、大小写字母_-|*@
     * @param receivers     分账接收方列表，不超过50个json对象，WxSharingModel
     *                      [
     *                      {
     *                      "type": "MERCHANT_ID",
     *                      "account":"190001001",
     *                      "amount":100,
     *                      "description": "分到商户"
     *                      },
     *                      {
     *                      "type": "PERSONAL_WECHATID",
     *                      "account":"86693952",
     *                      "amount":888,
     *                      "description": "分到个人"
     *                      }
     *                      ]
     * @return
     */
    public Map<String, String> multiProfitSharing(String transactionId, String outOrderNo, List<WxSharingModel> receivers) {
        byte[] certBytes = Base64Util.decode2Bytes(properties.getCertBase64());
        ByteArrayInputStream certInputStream = new ByteArrayInputStream(certBytes);
        Map<String, String> params = ProfitSharingModel.builder()
                .appid(properties.getAppId())
                .mch_id(properties.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .transaction_id(transactionId)
                .out_order_no(outOrderNo)
                .receivers(JsonUtil.toJson(receivers))
                .build()
                .createSign(properties.getApiKey(), SignType.MD5);
        String returnStr = WxPayApi.multiProfitSharing(params, certInputStream, properties.getCertPwd());
        return handleWxResult(returnStr);
    }

    /**
     * 分账查询
     *
     * @param transactionId 微信支付订单号
     * @param outOrderNo    查询分账结果，输入申请分账时的商户分账单号； 查询分账完结执行的结果，输入发起分账完结时的商户分账单号
     * @return
     */
    public Map<String, String> profitSharingQuery(String transactionId, String outOrderNo) {
        Map<String, String> params = OrderQueryModel.builder()
                .mch_id(properties.getMchId())
                .transaction_id(transactionId)
                .out_order_no(outOrderNo)
                .nonce_str(WxPayKit.generateStr())
                .build()
                .createSign(properties.getApiKey(), SignType.MD5);
        String returnStr = WxPayApi.profitSharingQuery(params);
        return handleWxResult(returnStr);
    }

    /**
     * 微信返回值处理方法
     *
     * @param str
     * @return
     */
    private Map<String, String> handleWxResult(String str) {
        Map<String, String> resultMap = PayKit.xmlToMap(str);
        String returnCode = resultMap.get("return_code");
//        String returnMsg = resultMap.get("return_msg");
        MDC.put(Const.TRACE_ID_KEY, "WX_PAY");
        if (WxPayKit.codeIsOk(returnCode)) {
            if (log.isDebugEnabled()) {
                log.info(JsonUtil.toJson(resultMap));
            }
        } else {
            log.error(JsonUtil.toJson(resultMap));
        }
        MDC.remove(Const.TRACE_ID_KEY);
        return resultMap;
    }
}
